package com.nulldev.util.web.nTubeExtensions.yte_for_archives.impl;

import static com.nulldev.util.web.nTube.pages.Urlizer.decodeUrlizer;

import java.util.LinkedHashMap;
import java.util.Map;

import com.nulldev.util.JVM.Arguments;
import com.nulldev.util.VariableAPI.ArrayUtils;
import com.nulldev.util.concurrency.threadIt.v4.emapi.cntft.ControllableFutureTask;
import com.nulldev.util.languages.json.uniapi.UniJsonValue;
import com.nulldev.util.logging.Logger;
import com.nulldev.util.logging.LoggerFactory;
import com.nulldev.util.manage.experiments.Experiments;
import com.nulldev.util.web.HttpClient.HttpClient;
import com.nulldev.util.web.HttpClient.HttpResponse;
import com.nulldev.util.web.HttpServer3.Response;
import com.nulldev.util.web.HttpServer3.pages.v3.ASyncHttpPage3;
import com.nulldev.util.web.URLUtils.URLQueryParser;
import com.nulldev.util.web.URLUtils.URLQueryParser.URLQueryParserOptions;
import com.nulldev.util.web.URLUtils.URLUtils;
import com.nulldev.util.web.legacyURI.LegacyURIUtils;
import com.nulldev.util.web.nTube.NTubeServer;

public class NTubeArchiveRedirectorPage extends ASyncHttpPage3 {

	public final static Logger log = LoggerFactory.getLoggerD(Arguments.hasArgument("--ProxyAPI.debug"), true);

	private static final boolean ENABLE_SECURITY_HARDENING = Experiments.get("ntube://enable-security-hardening/").isActive();

	private static final String[] ALLOWED_DOMAINS = new String[]
		{ ".*.youtube.com", };

	public NTubeArchiveRedirectorPage() {
		super("/archives/proxy");
	}

	@SuppressWarnings("resource")
	@Override
	public void onFutureRequest(final Request request, final ControllableFutureTask<Response> r) {
		final Map<String, String> s = URLQueryParser.extract(request.url(), URLQueryParserOptions.PREFER_EXPERIMENTAL,
				(URLUtils.USE_URL_ACCEL_V2 ? null : URLQueryParserOptions.DISABLE_URL_ACCEL), URLQueryParserOptions.DISABLE_DECODING,
				URLQueryParserOptions.TREAT_URL_AS_QUERY);
		if (!s.containsKey("url") && !(s.containsKey("uzm") && s.containsKey("uzc"))) {
			r.complete(new Response(404).requestKeepAliveDisable(true).header("Content-Length", "0"));
			return;
		} else if (s.containsKey("url") && s.get("url").isEmpty()) {
			r.complete(new Response(404).requestKeepAliveDisable(true).header("Content-Length", "0"));
			return;
		}
		final String url = decodeUrlizer(request.url());
		if (ENABLE_SECURITY_HARDENING && !ArrayUtils.StringArrayUtils.cardedContains(ALLOWED_DOMAINS, URLUtils.getDomain(url))) {
			log.warn("Blocking invalid URL: " + url);
			r.complete(new Response(401).requestKeepAliveDisable(true).header("Content-Length", "0"));
			return;
		}
		if (cache.containsKey(url)) {
			r.complete(new Response(307).header("Location", cache.get(url)));
			return;
		} else {
			try {
				final String _url = LegacyURIUtils.safeEncode(url);
				final HttpResponse<String> initial_resp = HttpClient.getString().url("https://archive.org/wayback/available?url=" + _url)
						.header("Via", "nTube " + NTubeServer.nTube_RELEASE_STRING).create();
				final UniJsonValue resp = UniJsonValue.parse(initial_resp.dataThenClose());
				resp.println();
				if (resp.contains("archived_snapshots")) {
					if (resp.get("archived_snapshots").contains("closest") && resp.get("archived_snapshots").get("closest").bool("available", false)) {
						final UniJsonValue obj = resp.get("archived_snapshots").get("closest");
						final String ar_url = obj.getString("url").replace("/http", "if_/http");
						cache.put(url, ar_url);
						r.complete(new Response(307).header("Location", ar_url));
					} else {
						r.complete(new Response(404).header("NTUBE_REASON", "ARCHIVE_NOT_AVAILABLE"));
					}
				} else {
					r.complete(new Response(404).header("NTUBE_REASON", "SNAPSHOTS_NOT_AVAILABLE"));
				}
			} catch (Exception _ex) {
				r.fail(_ex);
			}
		}
	}

	private final static int MAX = 128;
	private final Map<String, String> cache = new LinkedHashMap<String, String>(MAX) {
		private static final long serialVersionUID = 2546245625L;

		@Override
		public String get(final Object key) {
			synchronized (this) {
				final String _o = super.remove(key);
				return _o;
			}
		}

		@Override
		protected boolean removeEldestEntry(final Map.Entry<String, String> oldest) {
			return super.size() > MAX;
		}

	};
}
